
// import * as _ from "../lib/underscore.js";
import * as _ from "underscore";
import * as d3 from "d3";
import {sharedStore} from "../framework/sharedStore.js";
import {makeStrokeRenderer, makeLayerRenderer, makeDynamicRenderer} from "../canvas/twod.js";
import {report} from "../report.js";
import {$} from "../util/seq.js";

export function buildRenderer(rendererAgent, viewboxAgent, inputController, globeAgent, meshAgent) {

    const globe = globeAgent.value();
    const mesh = meshAgent.value();
    if (!globe || !mesh) {
        return;
    }

    report.status("Rendering Globe...");
    console.time("rendering map");

    const mapCtx = d3.select("#map").node().getContext("2d");
    const foregroundCtx = d3.select("#foreground").node().getContext("2d");
    const annotationSvg = d3.select("#annotation");
    const orientation = sharedStore.get("orientation");

    // UNDONE: have the globe do this itself
    globe.setOrientation(orientation, viewboxAgent.value());  // This allows the path function to work.

    const path = d3.geoPath().projection(globe.projection).pointRadius(d => d.size ?? 7);

    // Throttled draw method helps with slow devices that would get overwhelmed by too many redraw events.
    const REDRAW_WAIT = 5;  // milliseconds
    let doDraw_throttled = _.throttle(doDraw, REDRAW_WAIT, {leading: false});

    const LINE_WIDTH = 1.25;
    // CONSIDER: make these lines brighter/more opaque when viewing ocean layers
    const coastStyle = {strokeStyle: "rgba(250,250,250,0.65)", lineWidth: LINE_WIDTH};
    const lakesStyle = {strokeStyle: "rgba(250,250,250,0.65)", lineWidth: LINE_WIDTH};
    const riverStyle = {strokeStyle: "rgba(220,220,220,0.3)", lineWidth: LINE_WIDTH};

    const background = globe.backgroundRenderer();
    const graticule = globe.graticuleRenderer();
    const preForeground = globe.preForegroundRenderer();
    const postForeground = globe.postForegroundRenderer();
    const geoLo = [
        makeStrokeRenderer(mesh.riversLo, riverStyle),
        makeStrokeRenderer(mesh.lakesLo, lakesStyle),
        makeStrokeRenderer(mesh.coastLo, coastStyle),
    ];
    const geoHi = [
        makeStrokeRenderer(mesh.riversHi, riverStyle),
        makeStrokeRenderer(mesh.lakesHi, lakesStyle),
        makeStrokeRenderer(mesh.coastHi, coastStyle),
    ];
    const dynamicItems = makeDynamicRenderer();

    const mapRendererLo = makeLayerRenderer([background]);
    const mapRendererHi = makeLayerRenderer([background]);
    const fgRendererLo = makeLayerRenderer([preForeground, graticule, ...geoLo, dynamicItems, postForeground]);
    const fgRendererHi = makeLayerRenderer([preForeground, graticule, ...geoHi, dynamicItems, postForeground]);

    function canvasDraw(isHD) {
        const mapRenderer = isHD ? mapRendererHi : mapRendererLo;
        const foregroundRenderer = isHD ? fgRendererHi : fgRendererLo;
        mapRenderer.renderTo(mapCtx, path);
        foregroundRenderer.renderTo(foregroundCtx, path);
        path.context(null);
    }

    function doDraw() {
        annotationSvg.selectAll("path").attr("d", path);
        canvasDraw(false);
        rendererAgent.call("redraw");
        doDraw_throttled = _.throttle(doDraw, REDRAW_WAIT, {leading: false});
    }

    // Attach to map rendering events on input controller.

    inputController.on("moveStart.renderer", () => {
        rendererAgent.call("start");
    });
    inputController.on("move.renderer", () => {
        doDraw_throttled();
    });
    inputController.on("moveEnd.renderer", () => {
        annotationSvg.selectAll("path").attr("d", path);
        canvasDraw(true);
        rendererAgent.call("render");
    });

    // Finally, inject the globe model into the input controller. Do it on the next event turn to ensure
    // renderer is fully set up before events start flowing.
    Promise.resolve().then(() => {
        inputController.globe(globe);
    }).catch(report.error);

    console.timeEnd("rendering map");

    return {
        dynamicItems,
        draw() {
            annotationSvg.selectAll("path").attr("d", path);
            canvasDraw(true);
        },
    };
}

export function defineRendererBuilder(rendererAgent, viewboxAgent, inputController, globeAgent, meshAgent) {

    function startRendering() {
        rendererAgent.submit(() => buildRenderer(rendererAgent, viewboxAgent, inputController, globeAgent, meshAgent));
    }

    globeAgent.on($`update`, startRendering);
    meshAgent.on($`update`, startRendering);
}
